#include "tipo_colec.h"

TipoColec::TipoColec(const TipoDato &tipo_modelo, bool obligatorio): TipoDatoCompuesto(obligatorio){
		this->tipo_modelo = tipo_modelo.clonar();
}	
TipoColec::~TipoColec(){
	delete tipo_modelo;
}

void TipoColec::set_obligatorio(bool estado){
	this->obligatorio = estado;
}
	
TipoDato* TipoColec::get_tipo_modelo() const{
	return (this->tipo_modelo->clonar());
}

bool TipoColec::mismo_tipo(const TipoDato& dato) const{
	bool mismo_tipo = TipoDato::mismo_tipo(dato);
	if (mismo_tipo) {
		TipoColec* dato_colec = (TipoColec*) &dato;
		TipoDato* reg_modelo = (dato_colec->get_tipo_modelo());
		mismo_tipo = (this->tipo_modelo->mismo_tipo(*reg_modelo));
		delete (reg_modelo);
	}
	return mismo_tipo;
}
	
TipoDato& TipoColec::operator= (const TipoDato& dato) throw (ExcepErrorTipos){
	if (this == &dato)
		return *this;
	if (!this->mismo_tipo(dato))
		throw ExcepErrorTipos("Error - Se esperaba tipo coleccion");
		
	this->clear();
	
	std::vector< TipoDato* >::iterator it = ((TipoColec *)(&dato))->elementos.begin();
	while (it != ((TipoColec *)(&dato))->elementos.end()) {
		TipoDato* elem = (*it);
		this->add_dato(*elem);
		it++;
	}
	
	this->obligatorio = dato.es_obligatorio();
	return *this;	
}
	
bool TipoColec::operator== (const TipoDato& dato) const throw (ExcepErrorTipos){
	if (this->mismo_tipo(dato))
		throw ExcepErrorTipos("Error - Se esperaba tipo coleccion");
	
	const TipoDatoCompuesto * _dato = (TipoDatoCompuesto *) &dato;
	bool _return = TipoDatoCompuesto::elementos_iguales(_dato);
			
	return _return;
		
}
	
bool TipoColec::operator< (const TipoDato& dato) const throw (ExcepErrorTipos){
	if (this->mismo_tipo(dato))
		throw ExcepErrorTipos("Error - Se esperaba tipo coleccion");
	
	const TipoDatoCompuesto * _dato = (TipoDatoCompuesto *) &dato;
	bool _return = TipoDatoCompuesto::elementos_menores(_dato);
			
	return _return;
}

CodTipo TipoColec::get_codigo_tipo() const{
	return COLECCION;	
}

bool TipoColec::add_dato(const TipoDato & dato){
	//Me fijo si son del mismo tipo, sino devuelvo falso
	if (tipo_modelo->mismo_tipo(dato)) {
		//si no fallo, lo clono y lo agrego
		TipoDato* aux = dato.clonar();
		elementos.push_back(aux);
		return true;
	} else
		return false;
}
	 
bool TipoColec::quitar_dato(unsigned int ind){
	return TipoDatoCompuesto::quitar_dato(ind);
}

bool TipoColec::buscar_dato(const TipoDato& dato) const{
	bool encontrado = false;
	std::vector<TipoDato*>::const_iterator it = this->elementos.begin();
	while ((!encontrado) && (it != elementos.end())){
		TipoDato* aux = (*it);
		try {
			encontrado = ((*aux) == dato);
		} catch (ExcepErrorTipos &e) {
			return false;	
		}
		it++;
	}
	return encontrado;
}
	  
TipoDato& TipoColec::operator[] (size_t indice) throw (ExcepIndiceDesbordado){
	return (TipoDatoCompuesto::operator[](indice));
}
	
TipoDato* TipoColec::clonar() const{
	TipoColec* result = new TipoColec(*this->tipo_modelo, this->es_obligatorio() );
	copiar_elementos(result);
	return result;	
}
	
unsigned int TipoColec::get_tamanio() const{
	return elementos.size();	
}
	
ostream& TipoColec::toStream(ostream& out) const{
	
	out << "|";
	std::vector<TipoDato*>::const_iterator it = elementos.begin();
	while (it != elementos.end()){
		TipoDato* aux = (*it);	
		aux->toStream(out);
		it++;
		if (it != elementos.end())
			out << ',';
	}
	out << "|";
	if( es_obligatorio() )
		out << "+";
	else
		out << "*";
	return out;
		
}

void TipoColec::serializar(char ** retorno, unsigned int &tam){
	//si no tiene elementos, guardo el byte de presencia como 0 := false
	if(elementos.size() == 0 ){
		*retorno = new char[1];
		tam = 1;
		(*retorno)[0] = 0;
	}
	else{
		//pongo el bit de presencia en 1:= true y ademas guardo 4 bytes
		//para la cantidad de elementos
		tam = 1 + sizeof(unsigned int);
		*retorno = new char[tam];
		(*retorno)[0] = 1;
		
		unsigned int tam_aux;
		char * conv_aux;
		
		std::vector<TipoDato*>::const_iterator it = elementos.begin();
		while (it != elementos.end()){
			//convierto el tipo de dato
			(*it)->serializar(&conv_aux, tam_aux);
			//realoco la memoria para reservar mas espacio para lo nuevo.
			//Por ahora no reviso errores.
			
			char * aux_copia = new char[tam + tam_aux];
			memcpy(aux_copia, *retorno, tam);
			delete[] (*retorno);
			*retorno = aux_copia; 
			//copio el contenidont 
			memcpy( (*retorno) + tam, conv_aux, tam_aux);
			delete[] conv_aux;
			//incremento el espacio ocupado
			tam += tam_aux;
			//libero el auxiliar
						
			++it;
		}
		//guardo al comienzo, despues del byte de precencia, el tamano total
		memcpy((*retorno) + 1, &tam, sizeof(unsigned int));		
	}
			
}
		
bool TipoColec::deserializar(char * serializados, unsigned int tam){
	if(tam > 1){
		
		unsigned int pos_actual = 0;
		//reviso el byte de presencia primero
		char esta_presente;
		memcpy(&esta_presente,serializados, 1);
		pos_actual += 1;
		if(esta_presente == 1 ){
			unsigned int cant_elems;
			memcpy(&cant_elems, serializados + pos_actual, sizeof(unsigned int) );
			pos_actual += sizeof(unsigned int);
			//obtengo la cantidad de bytes que ocupa cada elemento de
			//la coleccion ya que todos tienen la misma
			char * cad_aux= NULL;
			unsigned int bytes_elem;
			while( pos_actual < cant_elems){
				memcpy(&bytes_elem, (serializados + pos_actual), sizeof(unsigned int));
				cad_aux = new char[bytes_elem];
				//obtengo un tipo de dato clon del modelo
				TipoDato * t_nuevo = tipo_modelo->clonar();
				//leo la cantidad de bytes que le tengo que meter
				memcpy(cad_aux,(serializados + pos_actual), bytes_elem);
				//deserializo y lo agrego a la coleccion
				t_nuevo->deserializar(cad_aux, bytes_elem);
				this->add_dato(*t_nuevo);
				delete t_nuevo;
				delete[] cad_aux;
				cad_aux = NULL;
				pos_actual += bytes_elem;
			}
			
		}
		return true;
	}
	return false;
}
